/* TODO:
    X delete a message
    X what about feed?
        - what happens when friend writes a message?
            - can they update the feed?
            - if so you are screwed

            - can we make it like entries?
                - profile render will have to be able to find the entries and aggregate them
                - isn't this just like the entries anyway?
                    - although you couldl argue that although it is duplication in this case,
                      it does provide a general purpose mechanism for feeds?

    X clear textarea after message is posted
    X bind/unbind for click events
    X only admin can update metadata?
    X nl2br 
    - make feeds more compatible with html output?
        - i.e. like photo album have a content field that can be rendered directly

    X loading anim for actions
    X check for empty message
    X refresh submits form?!
    X owner can delete own message even if not admin?
    X can delete other user's message if admin?

    X check works with group
    X some mechanism to get profile pictures into the messages?
    X delete-last-message bug?

    X just have feeds, no need for 'messages'?
    X prefs
        X change title
        - group delete role?
    - add 'public' option to settings
*/
var peerscape = null;
var app = function() {
    return {
        UUID_RE: /UUID/g,
        METADATA_PATH: '/content/metadata.json',
        //ENTRY_PATH_A: '/content/messages/entries/',
        //ENTRY_PATH_B: '/entry.json',
        POLICY_PATH: '/policy/sign',
        PUBLIC_POLICY: '*/content/feeds/entries/!/...',
        JSON_FEED_PATH: '/content/feeds/atom.json',
        FEED_PATH_A: '/content/feeds/entries/',
        FEED_PATH_B: '/entry.json',
        LINK_RE: /(http:\/\/[^\s]+)/,
        metadata: null,
        entries: null,
        entriesCache: null,
        isPublic: false,
        _debug: true,
        _loaded: false,
        _setup: function() {
            if (app.util.isAdmin(peerscape.contentInfo)) {
                // make sure the data is there. only really needed first time.
                var metadata = {
                    title: peerscape.contentInfo.title,
                    created: app.util.getTimestamp(),
                    updated: app.util.getTimestamp(),
                    author: {
                        name: peerscape.contentInfo.owner.name,
                        id: peerscape.contentInfo.owner.iid,
                    },
                    id: peerscape.contentInfo.iid,
                    generator: {
                        uri: peerscape.contentInfo.application.iid,
                        name: peerscape.contentInfo.application.name
                    },
                };
                //$.post('/postif'+app.METADATA_PATH, {value: $.toJSON(metadata)});
                $.ajax({
                    type: 'POST',
                    url: '/postif'+app.METADATA_PATH,
                    data: {value: $.toJSON(metadata)},
                    async: false
                });

                // make sure at least empty feeds exist
                //$.post('/postif'+app.JSON_FEED_PATH, {value: $.toJSON(app.util.generateJSONRedirFeed(metadata, app.FEED_PATH_A))});
                $.ajax({
                    type: 'POST',
                    url: '/postif'+app.JSON_FEED_PATH,
                    data: {value: $.toJSON(app.util.generateJSONRedirFeed(metadata, app.FEED_PATH_A))},
                    async: false
                });

                if (app.util.isGroup(peerscape.contentInfo)) {
                    // grant permissions for group admins to update metatdata
                    $.post('/grant/index', { who: peerscape.contentInfo.parent_profile.iid, pattern: app.METADATA_PATH, roles: '["admin"]'});

                    // grant permissions for group admins to delete entries
                    $.post('/grant/index', { who: peerscape.contentInfo.parent_profile.iid, pattern: '/content/feeds/...', roles: '["admin"]'});

                    // grant permissions for group members to write entries
                    $.post('/grant/index', { who: peerscape.contentInfo.parent_profile.iid, pattern: '/content/feeds/entries/!/*/entry.json'});
                }
                else {
                    // grant permissions for friends to write feed entries
                    $.post('/grant/index', { who: peerscape.contentInfo.owner.iid, pattern: '/content/feeds/entries/!/*/entry.json'});
                }
                
                // determine if the wall is public
                $.ajax({
                    type: 'GET',
                    url: '/list'+app.POLICY_PATH,
                    success: function(r) { 
                        r = $.secureEvalJSON(r);
                        for (var i=0; i<r.length; i++) {
                            if (r[i] == app.PUBLIC_POLICY) {
                                app.isPublic = true;
                                return;
                            }
                        }
                        app.isPublic = false;
                    },
                    error: function() { app.isPublic = false; }
                });

            }
        },
        screens: {},
        curScreen: null,
        init: function() {
            app.util._get_peerscape();

            // init the error dialog
            app.error.init();

            // show loading anim
            app.loading.init();
            app.loading.show();

            // call _setup
            app._setup();

            // init the main view
            app.home.init();

            // init the settings view
            app.settings.init();

            if (app.util.isAdmin(peerscape.contentInfo)) {
                $('#tabNav #homeTab a').eq(0)
                    .bind('click', function() {
                        //app.util.log('load 0');
                        app.load('home');
                        return false;
                    })
                    .show();
                $('#tabNav #settingsTab a')
                    .bind('click', function() {
                        //app.util.log('load 2');
                        app.load('settings');
                        return false;
                    })
                    .show();
            }
            else {
                $('#tabNav #homeTab a').hide();
                $('#tabNav #uploadTab a').hide();
                $('#tabNav #settingsTab a').hide();
            }
        },
        postInit: function() {
            $.historyInit(app.historyHandler);
            app._loaded = true;
        },
        load: function(hash) {
            //app.util.log('load: [' + hash + ']');
            $.historyLoad(hash);
        },
        historyHandler: function(hash) {
            if (hash && hash == 'noop') {
                return false;
            }
            if (app.curScreen != hash) {
                if (!hash || hash.indexOf('home') == 0) {
                    var page = ((hash && hash.indexOf(':') != -1) ? hash.split(':') : 1);
                    if (!page || page == undefined) {
                        page = 1;
                    }
                    else if (page != 1) {
                        page = page[1];
                    }
                    //app.util.log('PAGE: ' + page);

                    app.home.curPage = page;
                    app.home.refresh(true);
                    app.home.show();
                    peerscape.common.scrollToTop();
                }
                else if (hash == 'settings') {
                    app.settings.show();
                    peerscape.common.scrollToTop();
                }
                app.curScreen = hash;
            }
        },
        hideAll: function() {
            app.home.poll.stop();
            for (var s in app.screens) {
                app.screens[s].hide();
            }
        },
        error: {
            init: function() {
                $('#error .ok a').eq(0)
                    .bind('click', function() {
                        app.error.clear();
                        app.loading.hide();
                        return false;
                    });
            },
            set: function(msg) {
                $('#error').show().find('p.message').html(msg);
            },
            clear: function() {
                $('#error').hide().find('p.message').html('');
            }
        },
        getEntriesPath: function() {
            return app.FEED_PATH_A;
        },
        getSingleEntryPath: function(id) {
            return app.FEED_PATH_A + id + app.FEED_PATH_B;
        },
        getUserEntryPath: function(id, iid) {
            if (iid) {
                return app.FEED_PATH_A + iid + '/' + id + app.FEED_PATH_B;
            }
            return app.FEED_PATH_A + '!/' + id + app.FEED_PATH_B;
        },
        fetchEntries: function(successF, errorF) {
            $.ajax({
                type: 'GET',
                url: '/getall'+app.getEntriesPath(),
                success: successF,
                error: errorF
            });
        },
        fetchMetadata: function(successF, errorF) {
            $.ajax({
                type: 'GET',
                url: '/get'+app.METADATA_PATH,
                success: successF,
                error: errorF
            });
        },
        _processEntriesData: function(data) {
            // sort by date
            var ret = data.sort(function(a, b) {
                return (a.published < b.published);
            });

            if (!app.entriesCache) {
                app.entriesCache = {};
            }

            return ret;
        },
        /* -------------- HOME ---------------- */
        home: {
            ITEM_TEMPLATE: null,
            PAGER_ITEM_TEMPLATE: null,
            MESSAGES_PER_PAGE: 10,
            curPage: 1,
            numPages: 1,
            lastMark: 0,
            init: function() {
                // get the message template
                app.home.ITEM_TEMPLATE = $('#homeScreen #messageTemplate').clone();
                app.home.ITEM_TEMPLATE.removeAttr('id');

                // get the pager item template
                app.home.PAGER_ITEM_TEMPLATE = $('#homeScreen #pager .template').clone();
                app.home.PAGER_ITEM_TEMPLATE.removeAttr('class');

                // set the title
                app.home.setTitle(peerscape.contentInfo.title);
                app.screens['homeScreen'] = $('#homeScreen');

                // init the form
                $('#homeScreen #messageForm').submit(function() { return app.home.onsubmit(); });
                $('#homeScreen #messageForm input.submit')
                    .bind('click', function() {
                        app.home.save();
                        return false;
                    });
            },
            onsubmit: function() {
                /* validate the form */
                if (app.util.isEmpty($('#messageForm #message').val())) {
                    alert('Please enter a message');
                    return false;
                }
                return true;
            },
            save: function() {
                if (!app.home.onsubmit()) {
                    return false;
                }

                app.loading.show();

                // add an entry for this message
                var uuid = app.util.generateUID(peerscape.contentInfo.viewer.iid);
                var entry = {
                    title: null,
                    id: uuid,
                    published: app.util.getTimestamp(),
                    updated: app.util.getTimestamp(),
                    summary: peerscape.common.escapeHTML($('#messageForm #message').val()),
                    content: {
                        type: 'text',
                        value: peerscape.common.escapeHTML($('#messageForm #message').val()),
                    },
                    author: {
                        iid: peerscape.contentInfo.viewer.iid,
                        name: peerscape.contentInfo.viewer.name,
                        uri: 'http://'+peerscape.contentInfo.viewer.iid+'.hexlabel.net/'
                    },
                };
                $.ajax({
                    type: 'POST',
                    url: '/post'+app.getUserEntryPath(uuid, peerscape.contentInfo.viewer.iid),
                    data: { value: $.toJSON(entry) },
                    async: false
                });

                // add to local entries list
                app.entries.unshift(entry);

                // update the metadata
                // only admin can do this
                if (app.util.isAdmin(peerscape.contentInfo)) {
                    app.metadata.updated = app.util.getTimestamp();
                    $.post('/post'+app.METADATA_PATH, {value: $.toJSON(app.metadata)});
                }

                // clear input field
                $('#messageForm #message').val('');

                // refresh
                app.loading.hide();
                app.home.refresh();
            },
            deleteItem: function(id, author) {
                app.loading.show();
                for (var e in app.entries) {
                    if (app.entries[e].id == id) {
                        // remove the entry
                        $.ajax({
                            type: 'POST',
                            url: '/delete'+app.getUserEntryPath(id, author),
                            data: { value: 'something to put in post body' },
                            async: false
                        });

                        // remove from local entries list
                        app.entries.splice(e, 1);

                        // refresh
                        app.loading.hide();
                        app.home.refresh();
                        return;
                    }
                }

                // 'error: delete failed' goes here
                app.loading.hide();
                
            },
            setTitle: function(t) {
                $('#mainTitle').html(t);
            },
            refresh: function(bHard) {
                app.home.poll.stop();

                app.loading.show();
                if (bHard) {
                    // fetch the messages
                    app.fetchMetadata(
                        function(data) {
                            app.metadata = eval('(' + data + ')');
                        },
                        function (xhr, textStatus, errorThrown) {
                            // ignore
                        }
                    );
                    app.fetchEntries(
                        function(data) {
                            app.entries = app._processEntriesData(eval('(' + data + ')'));
                            app.home.render();
                            app.loading.hide();
                            ++app.home.lastMark;
                        },
                        function (xhr, textStatus, errorThrown) {
                            app.home.render();
                            app.loading.hide();
                            ++app.home.lastMark;

                            if (xhr.status == 404) {
                                app.error.set('Message information not found.');
                            }
                            else {
                                app.error.set('');
                            }
                        }
                    );
                }
                else {
                    app.home.render();
                    app.loading.hide();
                }
            },
            removeAll: function() {
                $('#messages').empty();
            },
            render: function() {
                if (app.entries.length > 0) {
                    $('#homeScreen .inner #none').hide();

                    app.home.removeAll();

                    var starti = (app.home.curPage - 1) * app.home.MESSAGES_PER_PAGE;
                    var endi = starti + app.home.MESSAGES_PER_PAGE;
                    if (endi > app.entries.length) {
                        endi = app.entries.length;
                    }
                    //app.util.log('home.r: '+ app.home.curPage + ' ('+starti+','+endi+')');
                    for (var e=starti; e<endi; e++) {
                        if (app.entries[e]) {
                            var row = app.home.ITEM_TEMPLATE.clone();
                            row.attr('class', 'message');

                            app.home._renderItem(row, app.entries[e]);

                            // add item to list
                            $('#homeScreen #messages').append(row);
                        }
                    }
                    peerscape.common.resize();
                }
                else {
                    app.home.removeAll();
                    $('#homeScreen .inner #none').show();
                }
                app.home.renderPager();

                app.home.poll.start();
            },
            _renderItem: function(item, entry) {
                item.attr('id', entry.id);

                if (app.util.isAdmin(peerscape.contentInfo) || peerscape.contentInfo.viewer.iid == entry.author.iid) {
                    item.find('.editNav a.delete').eq(0)
                        .bind('click', function() {
                            if (confirm('Are you sure you want to delete this message?')) {
                                app.home.deleteItem(entry.id, entry.author.iid);
                            }
                            return false;
                        })
                        .show();
                }
                else {
                    item.find('.editNav a.delete').eq(0)
                        .unbind('click')
                        .hide();
                }

                // populate message
                item.find('.body p').eq(0).html(app.util.nl2br(entry.content.value.replace(app.LINK_RE, '<a href="$1" target="_blank">$1</a>', 'gi')));

                item.find('.metadata p.updated').eq(0)
                    .html(app.util.formatTimestamp(entry.updated, true));

                item.find('p.from a').eq(0)
                    .attr('href', entry.author.uri)
                    .html(entry.author.name);


            },
            renderPager: function() {
                if (!app.entries || app.entries.length == 0) {
                    $('#homeScreen #pager').hide();
                    return;
                }
                
                app.home.numPages = Math.ceil(app.entries.length / app.home.MESSAGES_PER_PAGE);
                //app.util.log('['+app.entries.length + '][' + app.home.MESSAGES_PER_PAGE + '][' + app.home.numPages + ']');
                $('#homeScreen #pager p span').html(app.home.curPage + '/' + app.home.numPages);

                // insert pages
                $('#homeScreen #pager ul').empty();
                for (var p=1; p<=app.home.numPages; p++) {
                    var item = app.home.PAGER_ITEM_TEMPLATE.clone();
                    app.home._renderPagerItem(item, p);
                    $('#homeScreen #pager ul').append(item);
                }

                $('#homeScreen #pager').show();
            },
            _renderPagerItem: function(item, p) {
                item.find('a').eq(0)
                    .attr('href', '#home:' + p)
                    .html(p)
                    .bind('click', function() {
                        //app.util.log('will load: home:'+p);
                        app.load('home:' + p);
                        return false;
                    });

                // select current page link
                if (p == app.home.curPage) {
                    item.find('a').attr('class', 'selected');
                }
            },
            poll: {
                DELAY_MS: 30000,
                id: null,
                start: function() {
                    app.home.poll.id = setTimeout(app.home.poll.tick, app.home.poll.DELAY_MS);
                },
                stop: function() {
                    clearTimeout(app.home.poll.id);
                },
                tick: function() {
                    app.home.refresh(true);
                }
            },
            hide: function() {
                app.home.poll.stop();
                $('#homeScreen').hide();
            },
            show: function() {
                app.hideAll();
                $('#homeScreen').show();
                app.util.selectTabNav('homeTab');
                peerscape.common.resize();
            }
        },
        /* -------------- SETTINGS ---------------- */
        settings: {
            init: function() {
                $('#settingsForm').submit(function() { return false; });
                $('#settingsForm input.submit')
                    .bind('click', function() {
                        app.settings.save();
                        //app.util.log('load 12');
                        app.load('home');
                        return false;
                    });

                // register this screen
                app.screens['settingsScreen'] = $('#settingsScreen');
            },
            onsubmit: function() {
                if (app.util.isEmpty($('#settingsForm #title').val())) {
                    alert('Please enter a title');
                    return false;
                }
                return true;
            },
            save: function() {
                if (!app.settings.onsubmit()) {
                    return false;
                }
                app.loading.show();

                // set the title
                $.post('/content/title', {
                    value: peerscape.common.escapeHTML($('#settingsForm #title').val())
                });
                peerscape.contentInfo.title = peerscape.common.escapeHTML($('#settingsForm #title').val());
                app.home.setTitle(peerscape.common.escapeHTML($('#settingsForm #title').val()));
                if (app.metadata) {
                    app.metadata.title = peerscape.common.escapeHTML($('#settingsForm #title').val());

                    $.ajax({
                        type: 'POST',
                        url: '/post'+app.JSON_FEED_PATH,
                        data: {value: $.toJSON(app.util.generateJSONRedirFeed(app.metadata, app.FEED_PATH_A))},
                        async: false
                    });
                }

                // grant/revoke public permissions
                if ($('#settingsForm #public').attr('checked')) {
                    // grant permissions for anyone to write feed entries
                    $.post('/grant/index', { who: '*', pattern: '/content/feeds/entries/!/...'});
                    app.isPublic = true;
                }
                else {
                    // revoke permissions for anyone to write feed entries
                    $.post('/revoke/index', { who: '*', pattern: '/content/feeds/entries/!/...'});
                    app.isPublic = false;
                }

                app.loading.hide();
            },
            render: function() {
                $('#settingsForm #title').val(peerscape.common.unescapeHTML(peerscape.contentInfo.title));
                $('#settingsForm #public').attr('checked', app.isPublic);
            },
            hide: function() {
                $('#settingsScreen').hide();
            },
            show: function() {
                app.hideAll();
                app.loading.hide();
                app.settings.render();
                $('#settingsScreen').show();
                app.util.selectTabNav('settingsTab');
                peerscape.common.resize();
            }
        },
        /* -------------- LOADING ---------------- */
        loading: {
            init: function() {
                //app.screens['loading'] = $('#loading');
                app.loading.hide();
            },
            hide: function() {
                $('#loading').hide();
            },
            show: function() {
                $('#loading').show();
                peerscape.common.resize();
            }
        },
        /* -------------- UTIL ---------------- */
        util: {
            WS_RE: /^\s*$/,
            IMAGE_RE: /(\.jpg|\.jpeg|\.png|\.gif)$/i,
            log: function(s) {
                if (!app._debug) {
                    return;
                }
                try {
                    if (console != undefined) {
                        console.log(s);
                    }
                }
                catch(e) { /* nothing */ }
            },
            isEmpty: function(s) {
                return (!s || s.match(app.util.WS_RE));
            },
            isImage: function(s) {
                return (s && s.match(app.util.IMAGE_RE));
            },
            getTimestamp: function() {
                var now = new Date();
                yyyy = now.getUTCFullYear();
                mm = now.getUTCMonth();
                ++mm;
                if (mm <= 9) {
                    mm = '0' + mm;
                }
                dd = now.getUTCDate();
                if (dd <= 9) {
                    dd = '0' + dd;
                }
                hh = now.getUTCHours();
                if (hh <= 9) {
                    hh = '0' + hh;
                }
                mi = now.getUTCMinutes();
                if (mi <= 9) {
                    mi = '0' + mi;
                }
                ss = now.getUTCSeconds();
                if (ss <= 9) {
                    ss = '0' + ss;
                }
                return now.getUTCFullYear()+'-'+mm+'-'+dd+'T'+hh+':'+mi+':'+ss+'Z';
            },
            TS_RE: /(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)/,
            formatTimestamp: function(t, brk) {
                // 2009-11-26T14:3:35Z
                var months = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
                var parts = t.match(app.util.TS_RE);

                // this will create a date with local time offset
                var d = new Date(Date.UTC(parts[1], parseInt(parts[2])-1, parts[3], parts[4], parts[5], parts[6], 00))

                // get the 'pretty' representation
                var pretty = (brk ? ' <br/>' : ' ');
                prettyTmp = prettyDate(d.getFullYear()+'-'+(parseInt(d.getMonth()) + 1)+'-'+d.getDate()+'T'+d.getHours()+':'+d.getMinutes()+':00');
                if (prettyTmp != undefined) {
                    pretty += '(' + prettyTmp + ')';
                }
                else {
                    pretty = '';
                }

                // return in local format
                return d.toLocaleString() + pretty;
            },
            nl2br: function(s) {
                return s.replace(/\n/g, '<br/>');
            },
            selectTabNav: function(id) {
                $('#tabNav li').each(function() { $(this).removeAttr('class'); });
                $('#tabNav li#'+id).attr('class', 'selected');
            },
            imageLoader: function(id, src, target) {
                if (app.entriesCache && app.entriesCache[id]) {
                    //cache hit
                    target.attr('src', src).removeAttr('class');
                    peerscape.common.resize();
                }
                else {
                    var img = new Image();
                    img.src = src;
                    img.onload = function() {
                        target.attr('src', src).removeAttr('class');
                        peerscape.common.resize();
                    }
                    // damned opera
                    if ($.browser.opera) {
                        target.attr('src', src);
                        target.removeAttr('class');
                        peerscape.common.resize();
                    }
                }
            },
            _uidpart: function () {
                return(((1+Math.random())*0x10000)|0).toString(16).substring(1);
            },
            generateUID: function(iid) {
                var p = iid.substr(0,8)+'-'+iid.substr(8,4)+'-'+iid.substr(12,4)+'-'+iid.substr(16,4)+'-';
		        return (p+app.util._uidpart()+app.util._uidpart()+app.util._uidpart());
            },
            _get_peerscape: function() {
                try {
                    peerscape = parent.peerscape; 
                }
                catch (e) {
                    peerscape = null;
                }
                if (!peerscape) {
                    peerscape = {
                        contentInfo: {
                            parent_profile: {},
                            owner: {},
                            viewer: {},
                            application: {}
                        },
                        common: {
                            resize: function() {}
                        }
                    }
                }
            },
            /* FIXME: can we make this more comaptible with atom? */
            generateJSONRedirFeed: function(metadata, path) {
                if (!metadata) {
                    metadata = app.metadata;
                }
                var feed = {
                    title: metadata.title,
                    link: {
                        rel: 'self',
                        href: 'http://' + metadata.id + '.hexlabel.net/get/content/feed.json'
                    },
                    id: metadata.id,
                    updated: metadata.updated,
                    generator: {
                        uri: metadata.generator.uri,
                        name: metadata.generator.name
                    },
                    entries: [],
                    peerscape_redir: path
                }
                return feed;
            },
            generateJSONFeed: function(metadata) {
                if (!metadata) {
                    metadata = app.metadata;
                }
                var feed = {
                    title: metadata.title,
                    link: {
                        rel: 'self',
                        href: 'http://' + metadata.id + '.hexlabel.net/get/content/feed.json'
                    },
                    id: metadata.id,
                    updated: metadata.updated,
                    generator: {
                        uri: metadata.generator.uri,
                        name: metadata.generator.name
                    },
                    entries: []
                }
                for (var e in app.entries) {
                    var entry = {
                        title: app.entries[e].title,
                        id: app.entries[e].id,
                        published: app.entries[e].created,
                        updated: app.entries[e].updated,
                        summary: app.entries[e].summary,
                        content: {
                            type: 'text',
                            value: app.entries[e].content
                        },
                        author: {
                            name: app.entries[e].author.name,
                            uri: 'http://'+app.entries[e].author.iid+'.hexlabel.net/'
                        }
                    };
                    feed.entries.push(entry);
                }
                return feed;
            },
            /*
            generateXMLFeed: function() {
                return '<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom"></feed>';
            },
            */
            getImageType: function(src) {
                var ext = src.substr(src.lastIndexOf('.')+1);
                if (ext == 'jpg') {
                    ext = 'jpeg';
                }
                return 'image/'+ext;
            },
            isGroup: function(info) {
                return (info.parent_profile.type == 'group');
            },
            isAdmin: function(info) {
                // view is owner
                if (info.owner.iid == info.viewer.iid) {
                    return true;
                }

                // viewer is group admin
                if (info.parent_profile.type == 'group' && info.viewer.is_group_member) {
                    for (var r in info.viewer.group_roles) {
                        if (info.viewer.group_roles[r] == 'admin') {
                            return true;
                        }
                    }
                }

                // not an admin
                return false;
            }
        }
    }
}();
$(function() {
    app.init();
    app.postInit();
});

